﻿/// <summary>
/// Implements Glossary class
/// </summary>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
using SpaceSim.External;

namespace SpaceSim
{
    /// <summary>
    /// Primarily this object is the collection of AstroBodyDescs in the simulation. It then manages
    /// the modifiers for these AstroBodyDescs, which is how the app 1) randomizes SolarSystems in 
    /// the simulation and 2) applies rules for some AstroBodies (such as helium planets are only 
    /// possible when the system primary is a white dwarf).
    /// </summary>
    [XmlRootAttribute()]
    [Serializable]
    public class Glossary : Modifiable
    {
        #region Members configured by glossary.xml
        [XmlAttribute()]
        public int RandomSeed { get; set; }

        [XmlAttribute()]
        public int PlanetCount { get; set; }

        [XmlAttribute()]
        public double SelectionPenalty { get; set; }

        [XmlElement("AstroBodyDesc")]
        public AstroBodyDesc[] AstroBodyDescs;
        #endregion

        [XmlIgnore()]
        public Random Rand
        {
            get
            {
                if (null == _rand)
                {
                    _rand = RandomSeed > 0 ? new Random(RandomSeed) : new Random();
                }

                return _rand;
            }
        }

        public Glossary()
        {
            RandomSeed = -1;
            PlanetCount = 1;
            SelectionPenalty = 1;
        }

        /// <summary>
        /// Invokes all the Modifiers on this Glossary object making them apply their affects as
        /// specified in glossary.xml. Needed before code can begin the process of randomly filling
        /// a SolarSystem object for the simulation.
        /// </summary>
        /// <param name="parent">SolarSystem that is being filled.</param>
        public void ModifiersInitialize(SolarSystem parent)
        {
            Stack<object> ownershipStack = _CreateOwnershipStack(parent);
            base.Apply(Rand, ownershipStack, true);
        }

        /// <summary>
        /// Runs OnSelection modifiers for Modifiable property after it has been added to a 
        /// SolarSystem. This will also spin up an other Modifiers that affect, or use conditions,
        /// on the ownershipStack - rather than the most common Modifiers that only effect 
        /// themselves.
        /// </summary>
        /// <param name="parent">SolarSystem that is being filled.</param>
        public void ModifiersOnSelection(SolarSystem parent, Modifiable modifiable)
        {
            Stack<object> ownershipStack = _CreateOwnershipStack(parent);

            // First apply selection modifiers
            modifiable.OnSelection(Rand, ownershipStack);

            // Next tick the selected object again to re-randomize its values.
            modifiable.Apply(Rand, ownershipStack, true);

            // Finally do a non-fullUpdate of all objects in the glossary. This is what will make 
            // sure that external targets, which could now be valid because of our selection change,
            // get invoked.
            base.Apply(Rand, ownershipStack, false);
        }

        /// <summary>
        /// Internal helper method.
        /// 
        /// Creates a stack of with the given "parent" as the first element, and this Glossary 
        /// object as the second.
        /// </summary>
        /// <param name="parent">SolarSystem that is being filled.</param>
        private Stack<object> _CreateOwnershipStack(object parent)
        {
            Stack<object> ownershipStack = new Stack<object>();

            if (null != parent)
            {
                ownershipStack.Push(parent);
            }

            ownershipStack.Push(this);
            return ownershipStack;
        }

        /// <summary>
        /// Random object created with value from RandomSeed.
        /// </summary>
        [XmlIgnore()]
        private Random _rand;
    }
}
